package server

import (
	"context"
	"crypto/md5"
	"fmt"
	"io"
	"net/http"
	_ "net/http/pprof"
	"src/gcommon"

	"strconv"
	"sync"
	"time"

	"src/zeus/iserver"
	. "src/zeus/iserver"
	. "src/zeus/sess"
	"src/zeus/threads"

	"src/zeus/common"
	"src/zeus/dbservice"
	"src/zeus/entity"
	"src/zeus/etcd"
	"src/zeus/logger"
	"src/zeus/msgdef"
	"src/zeus/pool"
	"src/zeus/serverMgr"
)

// SrvNet 建立一个服务器的网关结构
type SrvNet struct {
	// srvType         ServerType2
	// srvID           uint64
	// sGroup          uint32  // 当前分组
	canConnectList  []ServerType2 // 可以连接的服务器类型
	WatchLocalTypes []ServerType2 //关注的本区的服务器类型
	oGroup          uint32        // 其他可以连接的分组
	oCanConnectList []ServerType2 // 其他分组中可以连接的服务器类型
	// addr            string
	// outerAddr       string //服务网外网地址
	// token           string
	// status          int
	console uint64

	IMsgServer //负责监听其它Server连接
	realptr    IServerCtrl

	//主动连接其他服务器的连接
	pendingSesses sync.Map // 等待连接的服务器连接,未验证的连接   key为服务器ID，value为ISess or server.ServerInfo
	clientSesses  sync.Map // 已连接的服务器连接,已验证的链接     key为服务器ID，value为ISess

	ServerInfo
	thgo *threads.ThreadGo

	// srvInfos *sync.Map
}

func NewSrvNetByInfo(srvType ServerType2, info *ServerBase, realptr IServerCtrl) (result *SrvNet) {
	result = new(SrvNet)
	result.thgo = threads.NewThreadGo()
	result.canConnectList = info.LocalType
	result.WatchLocalTypes = info.WatchLocalTypes
	result.oCanConnectList = info.GlobalType
	result.oGroup = info.Ogroupid
	result.IMsgServer = NewMsgServer("tcp", info.Inner)
	result.realptr = realptr
	debugaddr := ""
	if info.Debug {
		l, p := GetListener("tcp", info.Inner)
		debugaddr = fmt.Sprintf("%s:%d", info.Inner.Addr, p)
		logger.Infof("监听Pprof端口 : http://%v/debug/pprof", debugaddr)
		go func() {
			http.Serve(l, nil)
		}()
	}

	result.ServerInfo = ServerInfo{
		Number:       info.Number,
		ServerID:     info.SrvID,
		Group:        info.GroupID,
		Type:         srvType,
		InnerListen:  fmt.Sprintf("%s:%d", info.Inner.Listen, result.IMsgServer.GetPort()),
		InnerAddress: fmt.Sprintf("%s:%d", info.Inner.Addr, result.IMsgServer.GetEtcdPort()),
		OuterListen:  "",
		OuterAddress: "",
		HttpAddress:  "",
		DebugAddress: debugaddr,
		Console:      0,
		Load:         0,
		Token:        "",
		Status:       0,
	}

	return
}

func (srv *SrvNet) init() error {
	srv.IMsgServer.RegMsgProc(&SrvNet_Proc{srv})
	if err := srv.IMsgServer.Start(); err != nil {
		return err
	}

	if err := srv.registerSrvInfo(); err != nil {
		return err
	}
	etcd.GetEtcd().Start()
	/*
		etcd的注册和监听
	*/
	serverMgr.NewDiscoveryMgr(&srv.ServerInfo, nil)

	return nil
}

func (srv *SrvNet) CheckDuplicateByDiscovery() {
	//logger.Info("========================================================")
	//检测是否有本类型的服务,如果有,则可能是多开
	myPathKey := serverMgr.GetWatchPath(srv.Group, srv.Type)
	serverMgr.GetServerMgr().WatchByKey(myPathKey, serverMgr.NewServerMapByType())
	slist, err := serverMgr.GetServerMgr().GetServerListByType(srv.Group, srv.Type)
	if err == nil && len(slist) > 0 {
		logger.Debug(logger.LogKey_Boot, "发现本服务器多开信息:", len(slist))
		for _, v := range slist {
			logger.Info(gcommon.ToJsonString(v))
			if v.ServerID == srv.GetSrvID() {
				panic(fmt.Errorf("发现相同服务器已经启动,如需多开,请使用 -no 传递不同参数:sid=%v", v.ServerID))
			}
		}
		if srv.Type == gcommon.ServerTypeInter { //目前 此服务器不支持多开!
			panic("inter 服务器是不允许任何多开的!")
		}
		logger.Debug(logger.LogKey_Boot, "检测到可以多开,即将注册服务发现.")
	} else {
		logger.Debug(logger.LogKey_Boot, "未发现重复的服务器发现信息,即将注册服务发现.")
	}
	//srv.OnServerReady()
}

func (srv *SrvNet) WatchServer() {
	//监听服务器
	watchli := make([]ServerType2, 0, 10)
	for i := range srv.canConnectList { //关闭并连接 的服务器类型
		if common.ArrayIndexof(watchli, srv.canConnectList[i]) == -1 {
			watchli = append(watchli, srv.canConnectList[i])
		}
	}
	for i := range srv.WatchLocalTypes { //仅仅关注 但不连接 的服务器类型
		if common.ArrayIndexof(watchli, srv.WatchLocalTypes[i]) == -1 {
			watchli = append(watchli, srv.WatchLocalTypes[i])
		}
	}
	//监听本服
	for i := range watchli {
		serverMgr.GetServerMgr().Watch(srv.Group, watchli[i], nil)
	}
	//监听公共服 并 连接
	for i := range srv.oCanConnectList {
		serverMgr.GetServerMgr().Watch(srv.oGroup, srv.oCanConnectList[i], nil)
	}

	if len(watchli) == 0 {
	}
	srv.OnServerReady()
}

func (srv *SrvNet) regMsgProc(proc interface{}) {
	if srv.IMsgServer != nil {
		srv.IMsgServer.RegMsgProc(proc)
	}
}

// 注册当前服务器信息到redis，包括生成token等等
func (srv *SrvNet) registerSrvInfo() error {
	srv.Token = srv.genToken()
	// srv.regSrvInfo()
	return nil
}

func (srv *SrvNet) genToken() string {
	// if flags.GetFlagByBool(common.PrivateService) {
	// 	return "privateServiceToken"
	// }
	curtime := time.Now().Unix()
	h := md5.New()
	io.WriteString(h, strconv.FormatInt(curtime, 10))
	io.WriteString(h, strconv.FormatUint(srv.ServerID, 10))
	return fmt.Sprintf("%x", h.Sum(nil))
}

// 是否为自己需要连接的服务器
func (srv *SrvNet) isSrvList(info *ServerInfo) bool {
	if info.Group == srv.Group {
		return common.ArrayIndexof(srv.canConnectList, info.Type) != -1 //本区服服务器组中的 列表中
	} else if info.Group == srv.oGroup {
		return common.ArrayIndexof(srv.oCanConnectList, info.Type) != -1 //在跨服服务器组中的 列表中
	}
	return false
}

// 是否需要连接到某个服务器
func (srv *SrvNet) isNeedConnectedToSrv(info *ServerInfo) bool {
	return srv.isClientSess(info) && //相同类型不需要连接
		srv.isSrvList(info) && //服务器类型在列表中
		!srv.checkInConnectionList(info.ServerID) //之前没有连接过的服务器
}

func (srv *SrvNet) checkInConnectionList(srvID uint64) bool {
	_, ok := srv.pendingSesses.Load(srvID)
	if ok {
		logger.Warn("Server contecting, waiting response", srvID)
		return true
	}

	if is, ok := srv.clientSesses.Load(srvID); ok {
		s := is.(ISess)
		if s.IsClosed() {
			logger.Warnf("Server disconnected, ID:%d \n", srvID)
			srv.clientSesses.Delete(srvID)
		} else {
			return true
		}
	}

	return false
}

// GetSrvType 获取服务器类型
func (srv *SrvNet) GetSrvType() ServerType2 {
	return srv.Type
}

// GetSrvID 获取服务器ID
func (srv *SrvNet) GetSrvID() uint64 {
	return srv.ServerID
}

// GetSrvAddr 获取服务器内网地址
func (srv *SrvNet) GetSrvAddr() string {
	return srv.InnerAddress
}

func (srv *SrvNet) isClientSess(info *ServerInfo) bool {
	return srv.Type != info.Type
}

// 我启动完毕, 最好我连接别的服务器 和别人连接到我都准备好了 再调用~
func (srv *SrvNet) OnServerReady() {

}

func (srv *SrvNet) TryConnectToSrv(info *ServerInfo) {

	if !srv.isNeedConnectedToSrv(info) {
		return
	}
	var ss ISess
	var err error
	/*
		这里有二种可能
		1、之前的链接还在进行中，等他结束了。这边再尝试去链接
		2、之前的链接还在进行中，直接退出
	*/
	// 1、之前的链接还在进行中，等他结束了。这边再尝试去链接
	// for {
	// 	if k, ok := srv.pendingSesses.LoadOrStore(info.ServerID, v); !ok {
	// 		break
	// 	} else if f, ok := k.(func()); ok {
	// 		f()
	// 	}
	// }

	//2、之前的链接还在进行中，直接退出
	if s1, ok := srv.pendingSesses.LoadOrStore(info.ServerID, info); ok { //待连接的是一个serverInfo 不是 ISess
		logger.Warn("目标服务器正在连接中,忽略本次请求:", info.ServerID, s1)
		return
	}
	srv.thgo.Go(func(ctx context.Context) {
		defer func() {
			srv.pendingSesses.CompareAndDelete(info.ServerID, info) //如果连接失败,且还是serverInfo就删除
		}()
		ss, err = Dial("tcp", info.InnerAddress)
		if err != nil {
			logger.Errorf("[Client] Connect failed. %d to %d Addr %s, error:%v", srv.ServerID, info.ServerID, info.InnerAddress, err)
			return
		}

		ss.SetID(info.ServerID)
		ss.SetMsgHandler(srv.IMsgServer)
		ss.Send(&msgdef.ClientVerifyReq{
			Source: srv.Type,
			PID:    srv.ServerID,
			Token:  info.Token,
		})
		if srv.pendingSesses.CompareAndSwap(info.ServerID, info, ss) { //初始化完毕 如果还是serverInfo 就替换为 ISess
			ss.Start(srv.thgo)
			logger.Infof("[Client] SrvNet try connect to %d %v", info.ServerID, ServerType(info.Type).String())
		}
	})
}

// 我作为客户端, 连接到别的服务器 成功了
func (srv *SrvNet) onServerConnected(srvID uint64, stype ServerType2) {
	_ = srv.OnServerReady
	logger.Infof("[Client] Connected  Server succeed !!! %d %v ", srvID, ServerType(stype).String())

	// 已经验证过的链接,忽略处理
	if _, ok := srv.clientSesses.Load(srvID); ok {
		logger.Errorf("[Client] Server Session existed, server id: %d %d", srvID, stype)
		return
	}

	//不是等待连接的会话,忽略处理
	sess, ok := srv.pendingSesses.LoadAndDelete(srvID)
	if !ok {
		logger.Errorf("[Client] Session is pending, server id %d %d", srvID, stype)
		return
	}

	// srv.pendingSesses.Delete(srvID)
	srv.clientSesses.Store(srvID, sess)
}

// 我作为服务器, 其他客户端会话 连接到我的服务器 成功了
func (srv *SrvNet) onClientConnected(ii iserver.ISess) {
	_ = srv.OnServerReady
	var srvID uint64 = ii.GetID() //玩家ID或服务器ID
	var stype ServerType2 = ii.GetType()

	//通知对应的客户端,连接验证成功
	ii.Send(&msgdef.ClientVerifySucceedRet{
		Source:   0,
		PID:      0,
		SourceID: srv.ServerID,
		Type:     srv.Type,
	})
	logger.Infof("[SrvNet][Server]recevice a connect and verified success: %d %v", srvID, iserver.ServerType(stype).String())

	srv.realptr.ServerSessConnect(srvID, stype)
}

func (srv *SrvNet) destroy() {
	etcd.GetEtcd().Close()
	srv.IMsgServer.Close()
	srv.thgo.CloseWait()
	// info := &ServerInfo{
	// 	ServerID: srv.srvID,
	// }
	// serverMgr.GetServerMgr().Unregister(info)
}

// MainLoop 主循环
func (srv *SrvNet) MainLoop() {
	srv.IMsgServer.MainLoop()
}

// PostMsgToSrv 根据srvID号把消息投递到相应的服务器上
func (srv *SrvNet) PostMsgToSrv(srvID uint64, msg msgdef.IMsg) error {
	if srvID == GetSrvInst().GetSrvID() {
		srv.IMsgServer.FireMsg(msg.Name(), msg)
		return nil
	}

	// if srv.ServerID < srvID {
	isess, ok := srv.clientSesses.Load(srvID)
	if ok {
		isess.(ISess).Send(msg)
		return nil
	}
	// } else {
	isess = srv.IMsgServer.GetSession(srvID)
	if isess != nil {
		isess.(ISess).Send(msg)
		return nil
	}
	// }

	return fmt.Errorf("SrvNet server %d  Server not existed, id:%d", srv.ServerID, srvID)
}

// 发消息给客户端
func (srv *SrvNet) PostClient(eid uint64, srvID uint64, msg msgdef.IMsg) error {
	var packMsg msgdef.IMsg
	var err error
	if packMsg, err = entity.PackMsg(ServerTypeClient, eid, 0, msg); err != nil {
		return err
	}
	return srv.PostMsgToSrv(srvID, packMsg)
}

// 发消息给客户端
func (srv *SrvNet) PostClientByPID(pid uint64, msg msgdef.IMsg) error {
	red := dbservice.NewSessionUtil(pid)
	gateid, _ := red.GetGatewaySvrId()
	nttid, _ := red.GetUserEntityID()
	if gateid == 0 || nttid == 0 {
		return nil
	}
	return srv.PostClient(nttid, gateid, msg)
}

// IsSessExist 根据srvID判断服务器是否存在
func (srv *SrvNet) IsSessExist(srvID uint64) bool {
	//自己永远存在
	if srv.ServerID == srvID {
		return true
	}
	// if srv.ServerID < srvID {
	isess, ok := srv.clientSesses.Load(srvID)
	if ok {
		if !isess.(ISess).IsClosed() {
			return true
		}
		logger.Warnf("Server disconnected, ID:%d \n", srvID)
		srv.clientSesses.Delete(srvID)
	}
	// } else {
	isess = srv.IMsgServer.GetSession(srvID)
	if isess != nil {
		return true
	}
	// }

	return false
}

// PostMsgToSrvByType 根据sessType号把消息投递到相应的服务器上
func (srv *SrvNet) PostMsgToSrvByType(srvType ServerType2, group string, msg msgdef.IMsg) error {
	var tarSrvType ServerType2
	if srvType == ServerTypeClient {
		tarSrvType = ServerTypeGateway
	}
	if list, err := serverMgr.GetServerMgr().GetServerList(GetSrvInst().GetGroupId(), tarSrvType); err == nil {
		groupMsg, err := srv.packSrvGroupMsg(srvType, group, msg)
		if err != nil {
			return err
		}
		for _, va := range list {
			err = srv.PostMsgToSrv(va.ServerID, groupMsg)
			if err != nil {
				return err
			}
		}
		return nil
	} else {
		return err
	}
}

// PostMsgToSpace 将消息投递给某个Space
func (srv *SrvNet) PostMsgToSpace(srvID uint64, spaceID uint64, msg msgdef.IMsg) error {
	srvMsg, err := srv.packSrvMsg(spaceID, msg)
	if err != nil {
		return err
	}

	return srv.PostMsgToSrv(srvID, srvMsg)
}

// GetSrvIDBySrvType 获取一个SrvID (负载均衡获取)
func (srv *SrvNet) GetSrvIDBySrvType(srvType ServerType2) (uint64, error) {
	srvInfo, err := serverMgr.GetServerMgr().GetServerByType(GetSrvInst().GetGroupId(), srvType)
	if err != nil {
		return 0, err
	}

	return srvInfo.ServerID, nil
}

// 发消息给本地服务器指定类型,一般使用这个消息就是随边哪个服务器都可以,或是目标服务器就只有一个节点
func (srv *SrvNet) PostMsgTolocalSrvByType(srvType ServerType2, msg msgdef.IMsg) error {
	srvInfo, err := serverMgr.GetServerMgr().GetServerByType(GetSrvInst().GetGroupId(), srvType)
	if err != nil {
		return err
	}

	return srv.PostMsgToSpace(srvInfo.ServerID, 0, msg)
}

// 是否会连接此分组的服务器类型
func (srv *SrvNet) IsCanConnectBySrvType(group uint32, srvType ServerType2) (ok bool) {
	if srv.Group == group {
		return common.ArrayIndexof(srv.canConnectList, srvType) != -1
	} else if srv.oGroup != group {
		return common.ArrayIndexof(srv.oCanConnectList, srvType) != -1
	}
	return false
}

func (srv *SrvNet) packSrvMsg(spaceID uint64, msg msgdef.IMsg) (msgdef.IMsg, error) {
	//buf := make([]byte, MaxMsgBuffer)
	if msg.Size()+MsgHeadSize+10 > MaxMsgBuffer {
		//这个数据包好大
		logger.Warnf("SrvMsgTransport max size:%d", msg.Size())
		return nil, fmt.Errorf("SrvMsgTransport max size:%d", msg.Size())
		// return nil, nil
	}
	buf := pool.Get(msg.Size() + MsgHeadSize)
	defer pool.Put(buf)
	encBuf, err := EncodeMsg(msg, buf, true)
	if err != nil {
		return nil, err
	}
	msgContent := make([]byte, len(encBuf))
	copy(msgContent, encBuf)

	return &msgdef.SrvMsgTransport{
		SpaceID:    spaceID,
		MsgContent: msgContent,
	}, nil
}

func (srv *SrvNet) packSrvGroupMsg(srvType ServerType2, group string, msg msgdef.IMsg) (msgdef.IMsg, error) {
	buf := pool.Get(msg.Size() + MsgHeadSize)
	defer pool.Put(buf)
	encBuf, err := EncodeMsg(msg, buf, true)
	if err != nil {
		return nil, err
	}
	msgContent := make([]byte, len(encBuf))
	copy(msgContent, encBuf)
	return &msgdef.SrvGroupMsgTransport{
		SrvType:    srvType,
		Group:      group,
		MsgContent: msgContent,
	}, nil
}

// GetToken 获取服务器token
func (srv *SrvNet) GetToken() string {
	return srv.Token
}

// GetSrvInfo 获取服务器信息
func (srv *SrvNet) GetCurSrvInfo() *ServerInfo {
	return &srv.ServerInfo
}

// // GetLoad 获取服务器负载信息, 取CPU和内存的大值
// func (srv *SrvNet) GetLoad() int {
// 	var c int
// 	var vm int

// 	if loads, err := cpu.Percent(0, false); err == nil {
// 		if len(loads) > 0 {
// 			c = int(loads[0])
// 		}
// 	} else {
// 		logger.Error(err)
// 		c = 100
// 	}

// 	if memorys, err := mem.VirtualMemory(); err == nil {
// 		vm = int(memorys.UsedPercent)
// 	} else {
// 		logger.Error(err)
// 		vm = 100
// 	}

// 	if c > vm {
// 		return c
// 	}

// 	return vm
// }
